/*
 *    Copyright © OpenAtom Foundation.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */

package com.inspur.edp.bff.core.filter;

import com.inspur.edp.bef.api.lcp.IStandardLcp;
import com.inspur.edp.bef.spi.entity.builtinimpls.BefEntityResInfoImpl;
import com.inspur.edp.bef.spi.entity.builtinimpls.BefPropertyInfo;
import com.inspur.edp.bff.api.dataprocessor.IFilterFieldConvertor;
import com.inspur.edp.bff.api.exception.BffFrameworkException;
import com.inspur.edp.bff.api.exception.ErrorCodes;
import com.inspur.edp.bff.api.extend.IBffManagerExtend;
import com.inspur.edp.bff.api.manager.IFSManager;
import com.inspur.edp.bff.spi.builtin.BffEntityResInfoImpl;
import com.inspur.edp.bff.spi.builtin.BffPropertyInfo;
import com.inspur.edp.cef.spi.entity.info.propertyinfo.AssocationPropertyInfo;
import com.inspur.edp.cef.spi.entity.info.propertyinfo.BasePropertyInfo;
import com.inspur.edp.cef.spi.entity.info.propertyinfo.ComplexUdtPropertyInfo;
import com.inspur.edp.cef.spi.entity.info.propertyinfo.DataTypePropertyInfo;
import com.inspur.edp.cef.spi.entity.info.propertyinfo.ObjectType;
import com.inspur.edp.cef.spi.entity.info.propertyinfo.RefDataTypePropertyInfo;
import com.inspur.edp.cef.spi.entity.info.propertyinfo.SimpleAssoUdtPropertyInfo;
import com.inspur.edp.cef.spi.entity.info.propertyinfo.SimpleUdtPropertyInfo;
import com.inspur.edp.cef.spi.entity.resourceInfo.EntityResInfo;
import com.inspur.edp.cef.spi.entity.resourceInfo.ModelResInfo;
import com.inspur.edp.cef.spi.entity.resourceInfo.builinImpls.CefValueObjModelResInfo;
import com.inspur.edp.cef.spi.entity.resourceInfo.builinImpls.CefValueObjResInfo;
import com.inspur.edp.cef.spi.jsonser.base.StringUtils;
import com.inspur.edp.udt.api.Manager.IUdtManager;
import com.inspur.edp.udt.api.UdtManagerUtils;

import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;

public class FilterFieldConvertor implements IFilterFieldConvertor {

    private IFSManager manager;
    private Map<String, String> filterMap = new HashMap<>();
    private Map<String, BefEntityResInfoImpl> beEntityInfoMap = new HashMap<>();

    public FilterFieldConvertor(IFSManager fsManager) {
        this.manager = fsManager;
        initFilterDic();
    }

    @Override
    public String transFilterField(String fieldName) {
        //todo 处理虚拟vo
        if (manager.getBffContext().getSourceConfigs() == null || manager.getBffContext().getSourceConfigs().size() == 0)
            return fieldName;
        if (filterMap.containsKey(fieldName))
            return filterMap.get(fieldName);
        return fieldName;
    }

    private BefEntityResInfoImpl getBefEntityResInfo() {
        if (beEntityInfoMap.get("BefEntityResInfo") != null)
            return beEntityInfoMap.get("BefEntityResInfo");
        IStandardLcp lcp = manager.getBffContext().getLcp();
        String beEntityCode = manager.getBffContext().getBECodeFromVM(manager.getModelInfo().getRootNodeCode());
        EntityResInfo befEntityResInfo = lcp.getModelInfo().getCustomResource(beEntityCode);
        if (befEntityResInfo instanceof BefEntityResInfoImpl) {
            beEntityInfoMap.put("BefEntityResInfo", (BefEntityResInfoImpl) befEntityResInfo);
            return (BefEntityResInfoImpl) befEntityResInfo;
        } else
            throw new BffFrameworkException(ErrorCodes.BFF_FRAMEWORK_1023);
    }

    private Map<String, DataTypePropertyInfo> getBefPropertyInfos() {
        if (this.getBefEntityResInfo() == null) {
            return new HashMap<String, DataTypePropertyInfo>();
        }
        Map<String, DataTypePropertyInfo> befPropertyInfos = this.getBefEntityResInfo().getEntityTypeInfo().getPropertyInfos();
        return befPropertyInfos;
    }

    private BffEntityResInfoImpl getBffEntityResInfo() {
        ModelResInfo modelResInfo = manager.getModelInfo();
        String mainCode = modelResInfo.getRootNodeCode();
        BffEntityResInfoImpl bffEntityResInfo = (BffEntityResInfoImpl) modelResInfo.getCustomResource(mainCode);
        return bffEntityResInfo;
    }

    private Map<String, DataTypePropertyInfo> getPropertyInfos() {
        Map<String, DataTypePropertyInfo> propInfos = this.getBffEntityResInfo().getEntityTypeInfo().getPropertyInfos();
        return propInfos;
    }

    private void initFilterDic() {
        //todo 处理虚拟vo
        if (manager.getBffContext().getSourceConfigs() == null || manager.getBffContext().getSourceConfigs().size() == 0)
            return;
        initBasicFilterDic();
        initExtendFilterDic();
    }

    private void initBasicFilterDic() {
        Map<String, DataTypePropertyInfo> propertyInfos = this.getPropertyInfos();

        if (propertyInfos == null || propertyInfos.size() == 0)
            return;
        for (Map.Entry<String, DataTypePropertyInfo> map : propertyInfos.entrySet()) {
            DataTypePropertyInfo dataTypePropertyInfo = map.getValue();
            ObjectType type = dataTypePropertyInfo.getObjectType();

            String parentlabel = dataTypePropertyInfo.getPropertyName();
            if (isVirtualElement(dataTypePropertyInfo))
                continue;
            if (type == ObjectType.Association) {
                initAssociationElement(dataTypePropertyInfo, parentlabel, "");
                continue;
            }
            if (type == ObjectType.UDT) {
                initUdtElement(dataTypePropertyInfo, parentlabel, "");
                continue;
            }
            initNormalFilterDic(dataTypePropertyInfo, parentlabel);
        }
    }

    private void initNormalFilterDic(DataTypePropertyInfo typePropertyInfo, String voLabel) {
        String beLabelId = ((BffPropertyInfo) typePropertyInfo).getBeLabelId();
        initFilterDic(voLabel, beLabelId);
    }

    private void initExtendFilterDic() {
        List<IBffManagerExtend> bffExtends = manager.getBffContext().getExtList();
        if (bffExtends == null || bffExtends.size() == 0)
            return;
        for (IBffManagerExtend managerExtend : bffExtends) {
            managerExtend.mapFilterFromVo(filterMap);
        }
    }

    private boolean isVirtualElement(DataTypePropertyInfo propertyInfo) {
        boolean isVirtual = false;
        if (propertyInfo instanceof BffPropertyInfo) {
            String beLabelId = ((BffPropertyInfo) propertyInfo).getBeLabelId();
            if (beLabelId == null || "".equalsIgnoreCase(beLabelId))
                isVirtual = true;
        }
        return isVirtual;
    }

    private void initAssociationElement(DataTypePropertyInfo typePropertyInfo, String parentLabel, String returnValue) {
        //关联字段本身
        String beLabelId = ((BffPropertyInfo) typePropertyInfo).getBeLabelId();
        initFilterDic(parentLabel + "." + parentLabel, beLabelId);

        AssocationPropertyInfo assocationPropertyInfo = (AssocationPropertyInfo) typePropertyInfo.getObjectInfo();

        TreeMap<String, DataTypePropertyInfo> refPropInfos = assocationPropertyInfo.getAssociationInfo().getRefPropInfos();
        if (refPropInfos == null || refPropInfos.size() == 0)
            return;

        if (this.getBefPropertyInfos() == null || this.getBefPropertyInfos().size() == 0)
            return;
        DataTypePropertyInfo befPropInfo = this.getBefPropertyInfos().get(beLabelId);
        if (befPropInfo == null) {
            throw new BffFrameworkException(ErrorCodes.BFF_FRAMEWORK_1024, beLabelId);
        }

        AssocationPropertyInfo beAssoPropInfo = (AssocationPropertyInfo) befPropInfo.getObjectInfo();

        for (Map.Entry<String, DataTypePropertyInfo> refPropInfo : refPropInfos.entrySet()) {
            DataTypePropertyInfo refDataTypePropInfo = refPropInfo.getValue();

            String beElementLabelId = getBeElementLabelId(beAssoPropInfo, ((RefDataTypePropertyInfo) refDataTypePropInfo).getRefPropertyName());
            String childValue = isNullOrEmpty(returnValue) ? beElementLabelId : returnValue;

            String childLabel = isNullOrEmpty(parentLabel) ? refDataTypePropInfo.getPropertyName() : parentLabel;
            childLabel = childLabel + "." + refDataTypePropInfo.getPropertyName();

            if (refDataTypePropInfo.getObjectType() == ObjectType.UDT) {
                initUdtElement(refDataTypePropInfo, childLabel, childValue);
            } else {
                initFilterDic(childLabel, childValue);
            }
        }
    }

    private String getBeElementLabelId(AssocationPropertyInfo beAssoInfo, String refPropName) {
        String propName = "";
        for (Map.Entry<String, DataTypePropertyInfo> assoPorpInfo : beAssoInfo.getAssociationInfo().getRefPropInfos().entrySet()) {
            DataTypePropertyInfo dataTypePropInfo = assoPorpInfo.getValue();
            if (dataTypePropInfo instanceof RefDataTypePropertyInfo) {
                RefDataTypePropertyInfo refDataTypePropInfo = (RefDataTypePropertyInfo) dataTypePropInfo;
                if (refPropName.equals(refDataTypePropInfo.getRefPropertyName())) {
                    propName = refDataTypePropInfo.getPropertyName();
                    break;
                }
            }

            if (dataTypePropInfo instanceof BefPropertyInfo) {
                propName = getBeElementLabelIdByBePropInfo((BefPropertyInfo) dataTypePropInfo);
                if (refPropName.equals(propName)) {
                    propName = dataTypePropInfo.getPropertyName();
                    break;
                }
            }
        }
        return propName;

    }

    private String getBeElementLabelIdByBePropInfo(BefPropertyInfo befPropertyInfo) {
        String propName = befPropertyInfo.getPropertyName();
        String refPropName = propName.substring(propName.indexOf("_") + 1, propName.length());
        return refPropName;
    }

    private void initUdtElement(DataTypePropertyInfo typePropertyInfo, String parentLabel, String returnValue) {
        BasePropertyInfo propertyInfo = (BasePropertyInfo) typePropertyInfo.getObjectInfo();
        if (propertyInfo instanceof SimpleUdtPropertyInfo) {
            initSimpleUdtElement(typePropertyInfo, parentLabel, returnValue);
        } else {
            initComplexUdt(typePropertyInfo, (ComplexUdtPropertyInfo) propertyInfo, parentLabel);
        }

    }

    private void initComplexUdt(DataTypePropertyInfo typePropertyInfo, ComplexUdtPropertyInfo bffUdtPropertyInfo, String parentLabel) {
        String propLabel = typePropertyInfo.getPropertyName();
        CefValueObjResInfo udtResInfo = getUdtResInfo(bffUdtPropertyInfo.getUdtConfigId());
        Map<String, DataTypePropertyInfo> udtPropInfos = udtResInfo.getEntityTypeInfo().getPropertyInfos();
        //todo 暂时判断多值udt存为一列
        if (udtPropInfos.size() <= 1)
            return;
        if (typePropertyInfo instanceof BffPropertyInfo) {
            BffPropertyInfo bffPropertyInfo = (BffPropertyInfo) typePropertyInfo;
            BefPropertyInfo befPropertyInfo = (BefPropertyInfo) this.getBefPropertyInfos().get(bffPropertyInfo.getBeLabelId());
            //todo 目前虚拟字段是多值udt时，BffPropertyInfo上的beLabel属性有值，实际be上没有这个字段，返回不处理
            if (befPropertyInfo == null)
                return;
            ComplexUdtPropertyInfo befUdtPropInfo = (ComplexUdtPropertyInfo) befPropertyInfo.getObjectInfo();
            for (Map.Entry<String, DataTypePropertyInfo> typePropMap : befUdtPropInfo.getPropertyInfos().entrySet()) {
                DataTypePropertyInfo udtTypePropInfo = typePropMap.getValue();
                String beChildLabel = udtTypePropInfo.getPropertyName();
                String udtPropName = "";
                if (udtTypePropInfo instanceof RefDataTypePropertyInfo) {
                    udtPropName = ((RefDataTypePropertyInfo) udtTypePropInfo).getRefPropertyName();
                    initComplexUdtElement(propLabel, udtPropName, beChildLabel, parentLabel);
                }
                if (udtTypePropInfo instanceof BefPropertyInfo) {
                    udtPropName = getBeElementLabelIdByBePropInfo((BefPropertyInfo) udtTypePropInfo);
                    initComplexUdtElement(propLabel, udtPropName, beChildLabel, parentLabel);
                }

            }
        } else {
            for (Map.Entry<String, DataTypePropertyInfo> typePropMap : udtPropInfos.entrySet()) {
                DataTypePropertyInfo udtTypePropInfo = typePropMap.getValue();
                String udtPropName = udtTypePropInfo.getPropertyName();
                String beChildLabel = propLabel;
                initComplexUdtElement(propLabel, udtPropName, beChildLabel, parentLabel);
            }

        }


    }

    private void initComplexUdtElement(String propLabel, String udtPropName, String beChildLabel, String parentLabel) {
        String udtLabel = propLabel + "." + udtPropName;
        //如果有上级：关联 或者 关联UDT，非直接字段
        if (parentLabel.contains(".")) {
            String muLabel = parentLabel + "." + udtPropName;
            String mappDbLabel = beChildLabel + "_" + udtPropName;
            initFilterDic(muLabel, mappDbLabel);
        }
        initFilterDic(udtLabel, beChildLabel);
        initFilterDic(beChildLabel, beChildLabel);

    }

    private void initSimpleUdtElement(DataTypePropertyInfo typePropertyInfo, String parentLabel, String returnValue) {

        SimpleUdtPropertyInfo simpleUdtPropertyInfo = (SimpleUdtPropertyInfo) typePropertyInfo.getObjectInfo();
        CefValueObjResInfo udtResInfo = getUdtResInfo(simpleUdtPropertyInfo.getUdtConfigId());

        String udtLabel1 = parentLabel + "." + udtResInfo.getEntityCode();
        String udtLabel2 = udtLabel1 + "." + udtResInfo.getEntityCode();

        String childValue = isNullOrEmpty(returnValue) ? typePropertyInfo.getPropertyName() : returnValue;

        initFilterDic(udtLabel1, childValue);
        initFilterDic(udtLabel2, childValue);
        initFilterDic(typePropertyInfo.getPropertyName(), childValue);
        if (simpleUdtPropertyInfo instanceof SimpleAssoUdtPropertyInfo) {
            initAssoUdtElement(typePropertyInfo, udtLabel1, returnValue, udtResInfo);
        }
    }

    private void initAssoUdtElement(DataTypePropertyInfo typePropertyInfo, String parentLabel, String returnValue, CefValueObjResInfo udtResInfo) {

        SimpleAssoUdtPropertyInfo simpleAssoUdtPropertyInfo = (SimpleAssoUdtPropertyInfo) typePropertyInfo.getObjectInfo();
        TreeMap<String, DataTypePropertyInfo> refPropInfos = simpleAssoUdtPropertyInfo.getAssoInfo().getAssociationInfo().getRefPropInfos();
        if (refPropInfos == null || refPropInfos.size() == 0)
            return;
        for (Map.Entry<String, DataTypePropertyInfo> refPropInfo : refPropInfos.entrySet()) {
            DataTypePropertyInfo dataTypePropertyInfo = refPropInfo.getValue();
            String childValue = "";
            String childLabel = "";
            if (dataTypePropertyInfo instanceof RefDataTypePropertyInfo) {
                RefDataTypePropertyInfo refDataTypePropInfo = (RefDataTypePropertyInfo) refPropInfo.getValue();
                childValue = isNullOrEmpty(returnValue) ? refDataTypePropInfo.getPropertyName() : returnValue;
                childLabel = isNullOrEmpty(parentLabel) ? refDataTypePropInfo.getPropertyName() : parentLabel;
            } else {
                String propName = getBeElementLabelIdByBePropInfo((BefPropertyInfo) dataTypePropertyInfo);
                childValue = isNullOrEmpty(returnValue) ? propName : returnValue;
                childLabel = isNullOrEmpty(parentLabel) ? propName : parentLabel;
            }

            String udtRefPrpLabel = getUdtPropLabel(dataTypePropertyInfo.getPropertyName(), simpleAssoUdtPropertyInfo);

            if (udtRefPrpLabel != null) {
                childLabel = childLabel + "." + udtRefPrpLabel;
            } else {
                childLabel = childLabel + "." + dataTypePropertyInfo.getPropertyName();
            }

            if (dataTypePropertyInfo.getObjectType() == ObjectType.UDT) {
                //todo 多值ud后续处理
//                if(dataTypePropertyInfo.getObjectInfo() instanceof ComplexUdtPropertyInfo)
                initUdtElement(dataTypePropertyInfo, childLabel, childValue);
            } else {
                initFilterDic(childLabel, childValue);
            }
        }
    }

    private String getUdtPropLabel(String refPropName, SimpleAssoUdtPropertyInfo assoUdtPropertyInfo) {

        HashMap<String, String> assoMapping = assoUdtPropertyInfo.getMapping();
        for (Map.Entry<String, String> udtLabel : assoMapping.entrySet()) {
            if (refPropName.equals(udtLabel.getValue()))
                return udtLabel.getKey();
        }
        return null;
    }

    private void initFilterDic(String label, String returnValue) {
        filterMap.put(label, returnValue);

        if (!label.equals(StringUtils.toCamelCase(label))) {
            if (!filterMap.containsKey(StringUtils.toCamelCase(label))) {
                filterMap.put(StringUtils.toCamelCase(label), returnValue);
            }
        }

        String labelToCamel = toCamel(label);
        if (!filterMap.containsKey(labelToCamel)) {
            filterMap.put(labelToCamel, returnValue);
        }
    }

    private String toCamel(String propName) {
        if (!propName.contains("."))
            return StringUtils.toCamelCase(propName);
        String[] strArray = propName.split("\\.");
        int length = strArray.length;
        String propLabelName = "";
        for (int i = 0; i < length; i++) {
            propLabelName = propLabelName + StringUtils.toCamelCase(strArray[i]);
            if (i >= 0 && i < length - 1) {
                propLabelName = propLabelName + ".";
            }

        }
        return propLabelName;
    }

    private boolean isNullOrEmpty(String value) {
        return value == null || value.equals("");
    }

    private CefValueObjResInfo getUdtResInfo(String udtConfigid) {
        IUdtManager udtManager = (IUdtManager) UdtManagerUtils.getUdtFactory().createUdtManager(udtConfigid);
        CefValueObjModelResInfo valueObjModelResInfo = null;

        try {
            valueObjModelResInfo = (CefValueObjModelResInfo) udtManager.getClass().getMethod("getUdtModelInfo").invoke(udtManager);
            CefValueObjResInfo valueObjResInfo = (CefValueObjResInfo) valueObjModelResInfo.getCustomResource(valueObjModelResInfo.getRootNodeCode());
            return valueObjResInfo;
        } catch (IllegalAccessException e) {
            throw new BffFrameworkException(e);
        } catch (InvocationTargetException e) {
            throw new BffFrameworkException(e);
        } catch (NoSuchMethodException e) {
            throw new BffFrameworkException(e);
        }
    }
}
